package com.openatc.agent.service.area.impl;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.openatc.agent.model.Interfeature;
import com.openatc.agent.model.Route;
import com.openatc.agent.model.RouteIntersection;
import com.openatc.agent.model.RouteIntersectionBase;
import com.openatc.agent.model.area.Area;
import com.openatc.agent.resmodel.DevList;
import com.openatc.agent.service.AscsDao;
import com.openatc.agent.service.MessageService;
import com.openatc.agent.service.RouteDao;
import com.openatc.agent.service.area.AreaRepository;
import com.openatc.agent.service.area.AreaService;
import com.openatc.comm.common.CosntDataDefine;
import com.openatc.comm.data.MessageData;
import com.openatc.core.model.InnerError;
import com.openatc.core.model.RESTRet;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.model.model.area.AreaAscsBaseModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static com.openatc.core.common.IErrorEnumImplOuter.*;

/**
 * @Classname AreaServiceImpl
 * @Description
 * @Date 2022/9/5 11:14
 * @Created by panleilei
 */
// todo 后续区域接口都迁移至开源版
@Service
public class AreaServiceImpl implements AreaService {
    private final static Logger logger = Logger.getLogger(AreaServiceImpl.class.getSimpleName());

    @Autowired
    private AreaRepository areaDao;
    @Autowired
    private RouteDao routeDao;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private MessageService messageService;
    private Gson gson = new GsonBuilder().create();
    /**
     * 新增区域
     * @param areaEntity
     * @return
     */
    @Override
    public RESTRetBase addArea(Area areaEntity) {
        // 校验重复
        Area area = areaDao.findByName(areaEntity.getName());
        if (area != null) {
            return RESTRetUtils.errorObj(E_12001);
        }
        return RESTRetUtils.successObj(areaDao.save(areaEntity));
    }

    /**
     * 重命名区域
     * @param areaEntity
     * @return
     */
    @Override
    public RESTRetBase renameArea(Area areaEntity) {
        // 校验名称重复
        Area areaByName = areaDao.findByName(areaEntity.getName());
        if (areaByName != null) {
            return RESTRetUtils.errorObj(E_12001);
        }
        // 先根据id查询现有区域
        Area area = areaDao.findById(areaEntity.getId());
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        // 修改区域名字
        area.setName(areaEntity.getName());
        // 再保存
        areaDao.save(area);
        return RESTRetUtils.successObj(areaEntity);
    }

    /**
     * 更新区域信息
     * @param areaEntity
     * @return
     */
    @Override
    public RESTRetBase updateAreaInfo(Area areaEntity) {
        // 根据id查询区域
        Area area = areaDao.findById(areaEntity.getId());
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        area.setCycle(areaEntity.getCycle());
        area.setId(areaEntity.getId());
        area.setKeyintsid(areaEntity.getKeyintsid());
        area.setOptstrategy(areaEntity.getOptstrategy());
        area.setKeyrouteid(areaEntity.getKeyrouteid());
        // 更新区域
        area = areaDao.save(area);
        return RESTRetUtils.successObj(area);
    }

    /**
     * 更新区域独立路口
     * @param areaid
     * @param agentids
     * @return
     */
    @Override
    @Transactional
    public RESTRetBase updateAreaIsolate(Long areaid, List<String> agentids) {
        // 判断是否存在当前区域
        Area area = areaDao.findById(areaid);
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        // 把当前区域中的独立路口清空
        String clearSql = String.format("update dev set areaid = null where areaid = %s",areaid);
        jdbcTemplate.update(clearSql);
        // 更新dev表外键
        if (!CollectionUtils.isEmpty(agentids)) {
            String updateDevAreaId = String.format("update dev set areaid = %s where agentid in %s",areaid,AscsDao.getInCond(agentids));
            jdbcTemplate.update(updateDevAreaId);
        }
        return RESTRetUtils.successObj(areaDao.save(area));
    }

    /**
     * 根据id删除区域
     * @param id
     * @return
     */
    @Override
    @Transactional
    public RESTRetBase deleteArea(Long id) {
        areaDao.deleteById(id);
        // 同步dev表外键
        String sql = String.format("update dev set areaid = null where areaid = %s",id);
        jdbcTemplate.update(sql);
        logger.warning("Delete Area " + id);
        return RESTRetUtils.successObj();
    }

    /**
     * 更新区域中的路线
     * @param areaid
     * @param routeIds
     * @return
     */
    @Override
    @Transactional
    public RESTRetBase updateAreaRouteIDs(Long areaid, List<String> routeIds) {
        // 只需更新路线表中areaid即可
        Area area = areaDao.findById(areaid);
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        List<Route> routes = routeDao.findByAreaid(areaid);
        List<Long> ids = new ArrayList<>();
        for (String routeid : routeIds) {
            ids.add(Long.valueOf(routeid));
        }
        routes.addAll(routeDao.findByIdIn(ids));

        for (Route route : routes) {
            if (CollectionUtils.isEmpty(routeIds) || !ids.contains(route.getId())) {
                route.setAreaid(null);
                continue;
            }
            route.setAreaid(areaid);
        }

        return RESTRetUtils.successObj(routeDao.findByIdIn(ids));
    }

    /**
     * 获取所有区域
     * @return
     */
    @Override
    public RESTRetBase getAllArea() {
        List<Area> areas = areaDao.findAll();
        if (CollectionUtils.isEmpty(areas)) {
            return RESTRetUtils.successObj(areas);
        }
        Map<Long, Area> areaMap = areas.stream().collect(Collectors.toMap(Area::getId, Function.identity()));
        String queryDevSql = String.format("select d.areaid ,d.id,d.agentid,d.type,d.name,d.agentid,d.platform from t_area ta join dev d on d.areaid = ta.id");
        List<Map<String, Object>> devsMap = jdbcTemplate.queryForList(queryDevSql);
        if (!CollectionUtils.isEmpty(devsMap)) {
            List<AreaAscsBaseModel> devs = gson.fromJson(gson.toJson(devsMap),new TypeToken<List<AreaAscsBaseModel>>(){}.getType());
            for (AreaAscsBaseModel dev : devs) {
                long areaid = dev.getAreaid();
                Area area = areaMap.get(areaid);
                List<AreaAscsBaseModel> isolate = area.getIsolate();
                if (isolate == null) {
                    isolate = new ArrayList<>();
                    area.setIsolate(isolate);
                }
                isolate.add(dev);
            }
        }
        return RESTRetUtils.successObj(areas);
    }

    /**
     * 获取单个协调区域的信息
     * @param id
     * @return
     */
    @Override
    public RESTRetBase getAreaInfoById(Long id) {
        Area area = findAreaById(id);
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        return RESTRetUtils.successObj(area);
    }

    /**
     * 获取单个区域信息
     * @param areaId
     * @return
     */
    public Area findAreaById(Long areaId) {
        Area area = areaDao.findById(areaId);
        if (area == null) return null;
        List<AreaAscsBaseModel> areaIsolate = findAreaIsolate(areaId);
        area.setIsolate(areaIsolate);
        return area;
    }


    /**
     * 获取单个区域的独立路口
     * @param areaid
     * @return
     */
    private List<AreaAscsBaseModel> findAreaIsolate(Long areaid) {
        String queryAreaDev = String.format("select areaid,id,agentid,type,name,agentid,platform from dev where areaid = %s",areaid);
        List<Map<String, Object>> devsMap = jdbcTemplate.queryForList(queryAreaDev);
        List<AreaAscsBaseModel> isolate = new ArrayList<>();
        if (CollectionUtils.isEmpty(devsMap)) return isolate;
        isolate = gson.fromJson(gson.toJson(devsMap), new TypeToken<List<AreaAscsBaseModel>>() {
        }.getType());
        return isolate;
    }

    /**
     * 获取所有协调区域的简易信息
     * @return
     */
    @Override
    public RESTRetBase getAreaSimpleInfo() {
        return RESTRetUtils.successObj(areaDao.findAreaSimpleInfo());
    }

    /**
     * 下载区域方案
     * @param jsonObject
     * @return
     */
    @Override
    public RESTRetBase saveAreaPattern(JsonObject jsonObject) {
        //先拿到pattern的jsonObject对象feature
        JsonArray devlistJsonArray = jsonObject.get("devs").getAsJsonArray();
        if (devlistJsonArray == null || devlistJsonArray.isJsonNull()) {
            return RESTRetUtils.errorObj(E_1000);
        }
        for (JsonElement device : devlistJsonArray) {
            String intersectionid = device.getAsJsonObject().get("agentid").getAsString();
            //featureList就是要发送的patternList
            JsonObject featureList = device.getAsJsonObject().get("feature").getAsJsonObject();
            MessageData messageData = new MessageData(intersectionid, CosntDataDefine.setrequest, "feature/pattern",featureList);
            RESTRet retBase = messageService.postDevsMessage(messageData);
            if (!retBase.isSuccess()) {
                return retBase;
            }
        }
        return RESTRetUtils.successObj();
    }

    /**
     * 查询区域整体方案
     * @param id
     * @param feature1
     * @return
     */
    @Override
    public RESTRetBase getAreaPattern(Long id, String feature1) {
        // 获取该区域中独立路口
        Area area = findAreaById(id);
        if (area == null) {
            return RESTRetUtils.errorObj(E_12002);
        }
        Set<String> agentIdList = getAgentIdList(area.getIsolate(),area.getRoutes());
        // 返回消息的最外层结构
        DevList devList = new DevList();
        List<RouteIntersectionBase> devs = new ArrayList<>();
        for (String agentid : agentIdList) {
            RouteIntersectionBase eachDev = new RouteIntersectionBase();
            eachDev.setAgentid(agentid);

            RESTRet ret = getFeaturePattern(agentid,feature1);
            if (!ret.isSuccess() || ret.getData() == null) {
                InnerError iErrorEnum = gson.fromJson(gson.toJson(ret.getData()), InnerError.class);
                return RESTRetUtils.errorDetialObj(E_4002, iErrorEnum);
            }
            JsonObject jsonObject = gson.fromJson(gson.toJson(ret.getData()), JsonObject.class);
            Interfeature feature = new Interfeature();
            if ("pattern".equals(feature1)) {
                JsonElement jsonElement = jsonObject.get("data").getAsJsonObject().get("patternList");
                feature.setPatternList(jsonElement);
            }
            if ("phase".equals(feature1)) {
                JsonElement jsonElement = jsonObject.get("data").getAsJsonObject().get("phaseList");
                feature.setPhaseList(jsonElement);
            }
            eachDev.setFeature(feature);
            devs.add(eachDev);
        }
        devList.setDevs(devs);
        return RESTRetUtils.successObj(devList);
    }

    /**
     * 获取当前区域中所有的路口id
     * @param isolate
     * @param routes
     * @return
     */
    private Set<String> getAgentIdList(List<AreaAscsBaseModel> isolate, List<Route> routes) {
        Set<String> agentids = new HashSet<>();
        if (!CollectionUtils.isEmpty(isolate)) {
            for (AreaAscsBaseModel dev : isolate) {
                agentids.add(dev.getAgentid());
            }
        }
        if (!CollectionUtils.isEmpty(routes)) {
            for (Route route : routes) {
                Set<RouteIntersection> devs = route.getDevs();
                for (RouteIntersection dev : devs) {
                    agentids.add(dev.getAgentid());
                }
            }
        }

        return agentids;
    }

    /**
     * 获取路口的方案
     * @param agentid
     * @param feature
     * @return
     */
    private RESTRet getFeaturePattern(String agentid, String feature) {
        // 1、先获取缓存
        MessageData messageData = new MessageData(agentid,"get-request","feature/" + feature);
        return messageService.postDevsMessage(messageData);
    }
}
